<template>
  <div>
    <button class="upload-btn">
      <input type="file" class="select-file-input" οnclick="f.outerHTML=f.outerHTML" @change="handleFileChange" multiple />
      选择文件
    </button>
    <div>
      <button @click="handleUpload">上传文件</button>
    </div>
    <div>
      <h3>文件列表</h3>
      {{ uploadFiles }}
      <ul>
        <li v-for="(item, index) in uploadFiles" :key="index">{{ item.name }}</li>
      </ul>
    </div>
  </div>
</template>

<script setup>
// 所有文件状态
const Status = {
  wait: "wait",
  pause: "pause",
  uploading: "uploading",
  hash: "hash",
  error: "error",
  done: "done"
};

// 单个文件的状态
const fileStatus = {
  wait: "wait",
  uploading: "uploading",
  success: "success",
  error: "error",
  secondPass: "secondPass",
  pause: "pause",
  resume: "resume"
};
// 单个文件的状态 对应描述
const fileStatusStr = {
  wait: "待上传",
  uploading: "上传中",
  success: "成功",
  error: "失败",
  secondPass: "已秒传",
  pause: "暂停",
  resume: "恢复"
};

let chunkSize = 10 * 1024 * 1024; // 切片大小
let fileIndex = 0; // 当前正在被遍历的文件下标

let uploadFiles = ref([]); // 上传的文件列表
const status = ref(Status.wait); // 所有文件上传状态
let worker = ref(null);

// 选择文件
const handleFileChange = e => {
  const files = e.target.files;
  if (!files) return;
  console.log("选择的文件", files);
  fileIndex = 0; // 重置文件下标
  status.value = Status.wait;
  console.log("wait---所有文件待上传");
  const postFiles = Array.prototype.slice.call(files);
  // 这里可以根据条件过滤一下，再push进uploadFiles
  postFiles.forEach(item => {
    item.status = fileStatus.wait;
    item.chunkList = [];
    item.uploadProgress = 0;
    item.fakeUploadProgress = 0;
    item.hashProgress = 0;

    uploadFiles.value.push(item);
  });
};

// 上传文件
const handleUpload = async () => {
  if (!uploadFiles.value) return;
  status.value = Status.uploading;
  console.log("uploading---所有文件上传中");
  const filesArr = uploadFiles.value;

  // 遍历请求文件上传
  for (let i = 0; i < filesArr.length; i++) {
    fileIndex = i;
    // if (["secondPass", "success", "error"].includes(filesArr[i].status)) {
    //   console.log("跳过已上传成功或已秒传的或失败的");
    //   continue;
    // }
    const fileChunkList = createFileChunk(filesArr[i]);
    status.value = Status.hash;
    filesArr[i].hash = await calculateHash(fileChunkList);

    filesArr[i].chunkList = fileChunkList.map(({ file }, index) => ({
      fileHash: filesArr[i].hash,
      fileName: filesArr[i].name,
      index,
      hash: filesArr[i].hash + "-" + index,
      chunk: file,
      size: file.size
      //   uploaded: getChunkStorage && getChunkStorage.includes(index), // 标识：是否已完成上传
      //   progress: getChunkStorage && getChunkStorage.includes(index) ? 100 : 0,
      //   status: getChunkStorage && getChunkStorage.includes(index) ? "success" : "wait" // 上传状态，用作进度状态显示
    }));
    console.log(filesArr[i].name + "---创建完hash文件和切分文件", filesArr[i]);
    console.log(uploadFiles);
    // 上传分片
    await uploadChunks(filesArr[i]);
  }
};

// 文件分片
const createFileChunk = (file, size = chunkSize) => {
  console.log(`${file.name}------文件创建分片`);
  const fileChunkList = [];
  let count = 0;
  while (count < file.size) {
    fileChunkList.push({
      file: file.slice(count, count + size)
    });
    count += size;
  }
  console.log("创建分片成功", fileChunkList);
  return fileChunkList;
};

// 生成文件 hash（web-worker）
const calculateHash = fileChunkList => {
  console.log("文件创建哈希值", fileChunkList);
  return new Promise(resolve => {
    worker.value = new Worker("./hash.js");
    worker.value.postMessage({ fileChunkList });
    worker.value.onmessage = e => {
      const { percentage, hash } = e.data;
      if (uploadFiles.value[fileIndex]) {
        uploadFiles.value[fileIndex].hashProgress = Number(percentage.toFixed(0));
        // this.$set(this.uploadFiles, fileIndex, this.uploadFiles[fileIndex]);
      }

      if (hash) {
        resolve(hash);
      }
    };
  });
};

// 分片上传服务器
const uploadChunks = file => {
  console.log(`${file.name}-----开始上传分片`);
  const chunkData = file.chunkList;
  return new Promise(async (resolve, reject) => {
    // 这里要调接口先过滤调已上传的分片
    const chunkDataParams = chunkData
      .filter(({ uploaded }) => !uploaded)
      .map(({ fileHash, chunk, fileName, index }) => {
        const formData = new FormData();
        formData.append("md5", fileHash);
        formData.append("file", chunk);
        formData.append("fileName", index); // 文件名使用切片的下标
        return { formData, index, fileName };
      });

    console.log(`${file.name}-----开始并发上传分片`, chunkDataParams);
    // 并发上传切片
    try {
      const ret = await sendRequest(chunkDataParams, chunkData);

      //如果所有切片合并成功则发起合并分片请求
      console.log(`${file.name}-----所有分片上传成功，发起合并请求`);
      await mergeRequest(data);
      // const isUpload =
    } catch (error) {
      // 上传有被reject的
      console.error(`${file.name}-----存在上传失败的分片文件`, error);
      return;
    }
  });
};

// 并发上传
const sendRequest = (forms, chunkData) => {};

// 发起合并分片
const mergeRequest = data => {};
</script>

<style lang="scss">
.upload-btn {
  width: 96px;
  height: 28px;
}
.select-file-input {
  position: absolute;
  display: inline-block;
  left: 0;
  top: 0;
  border: none;
  opacity: 0;
  width: 96px;
  height: 28px;
}
</style>
